#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
from ._util import Util, ActionRunner
from ._const import Version
from ._prototype import WindowsInstallationMediaBase
from ._errors import InstallMediaError, BuildError
from ._settings import Settings, TargetSettingsBase
from . import os_win98, os_winxp, os_win7


class Builder(ActionRunner):

    def __init__(self, settings, target_settings, work_dir):
        assert Settings.check_object(settings, raise_exception=False)
        assert isinstance(target_settings, TargetSettingsBase) and target_settings.check(raise_exception=False)

        self._s = settings
        if self._s.log_dir is not None:
            os.makedirs(self._s.log_dir, mode=0o750, exist_ok=True)

        self._ts = target_settings

        self._workDirObj = work_dir
        if self._ts.version == Version.WINDOWS_98:
            self._workDirObj.attach_addon(os_win98._build.WorkDirAddon(self._workDirObj))
        elif self._ts.version == Version.WINDOWS_XP:
            self._workDirObj.attach_addon(os_winxp._build.WorkDirAddon(self._workDirObj))
        elif self._ts.version == Version.WINDOWS_7:
            self._workDirObj.attach_addon(os_win7._build.WorkDirAddon(self._workDirObj))
        else:
            assert False

        actionList = [
            self.action_prepare,
            self.action_install_os,
            self.action_install_applications,
            self.action_cleanup,
        ]

        def x(self, action_name, action):
            action.run(action_name, self._s, self._ts, self._workDirObj)

        super().__init__(self._workDirObj._persistentStorage, actionList, x, BuildError)

    @ActionRunner.Action()
    def action_prepare(self, install_media):
        assert isinstance(install_media, WindowsInstallationMediaBase)

        spec = None
        if self._ts.version == Version.WINDOWS_98:
            spec = os_win98.Spec()
        elif self._ts.version == Version.WINDOWS_XP:
            spec = os_winxp.Spec(self._ts.arch)
        elif self._ts.version == Version.WINDOWS_7:
            spec = os_win7.Spec(self._ts.arch)
        else:
            assert False

        # check install iso file
        if self._ts.arch != install_media.get_arch():
            raise InstallMediaError("invalid install ISO file, arch not match")
        if self._ts.version != install_media.get_version():
            raise InstallMediaError("invalid install ISO file, version not match")
        if self._ts.edition not in install_media.get_editions():
            raise InstallMediaError("invalid install ISO file, edition not match")
        if self._ts.lang not in install_media.get_languages():
            raise InstallMediaError("invalid install ISO file, language not match")

        # create real install iso file
        if self._ts.version == Version.WINDOWS_98:
            os_win98._build.BuildUtil.prepare(self._ts, self._workDirObj, install_media)
        elif self._ts.version == Version.WINDOWS_XP:
            os_winxp._build.BuildUtil.prepare(self._ts, self._workDirObj, install_media)
        elif self._ts.version == Version.WINDOWS_7:
            os_win7._build.BuildUtil.prepare(self._ts, self._workDirObj, install_media)
        else:
            assert False

        # save target settings
        self._workDirObj._saveTargetSettings(self._ts)

        # create and partition disk image file
        self._workDirObj._createImageFile(Util.diskSizeStrToInt(spec.getMainDiskSize()), spec.getStorageLayoutName())

    @ActionRunner.Action(after=["prepare"])
    def action_install_os(self):
        if self._ts.version == Version.WINDOWS_98:
            os_win98._build.BuildUtil.osInstall(self._ts, self._workDirObj, not self._getQuiet())
        elif self._ts.version == Version.WINDOWS_XP:
            os_winxp._build.BuildUtil.osInstall(self._ts, self._workDirObj, not self._getQuiet())
        elif self._ts.version == Version.WINDOWS_7:
            os_win7._build.BuildUtil.osInstall(self._ts, self._workDirObj, not self._getQuiet())
        else:
            assert False

    @ActionRunner.Action(after=["install_os"])
    def action_install_applications(self):
        pass

    @ActionRunner.Action(after=["install_os", "install_applications"])
    def action_cleanup(self):
        if self._ts.version == Version.WINDOWS_98:
            os_win98._build.BuildUtil.cleanup(self._ts, self._workDirObj)
        elif self._ts.version == Version.WINDOWS_XP:
            os_winxp._build.BuildUtil.cleanup(self._ts, self._workDirObj)
        elif self._ts.version == Version.WINDOWS_7:
            os_win7._build.BuildUtil.cleanup(self._ts, self._workDirObj)
        else:
            assert False

    def add_custom_actions(self, action_dict, insert_after=None, insert_before=None):
        for action in action_dict.values():
            assert action.check_object(action, raise_exception=False)
        super().add_custom_actions(action_dict, insert_after, insert_before)

    def _getQuiet(self):
        return (self._s.verbose_level == 0)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.dispose()
